
# Environment
CXX         = g++
OUTPUT_DIR  = bin
BUILD_DIR   = build
OBJECT_DIR  = $(BUILD_DIR)/obj
#MAIN_OBJECT_DIR  := $(BUILD_DIR)/main/obj
#TEST_OBJECT_DIR := $(BUILD_DIR)/test/obj
TEST_EXEC_DIR   := $(BUILD_DIR)/bin

# Compile Flags
COMPILE_FLAGS := -std=c++11 -ggdb3 -pipe -pthread -Wall -Wextra -Werror \
                 -O3 -fno-strict-aliasing -fno-tree-vectorize -Iinclude/tux

# Callable variables (i.e. functions)
find_in_dir_with_suffix = $(foreach dir,$(1),$(shell find $(dir) -name "*.$(2)"))
dependencies = -MMD -MF $(1:.o=.d) -MT $(1)
compile = $(CXX) -c $(1) -o $(OBJECT_DIR)/$(2) $(call dependencies,$(OBJECT_DIR)/$(2)) $(COMPILE_FLAGS)
echo_and_execute = echo $(1); $(1)

# Main
MAIN_SOURCE_ROOT_DIR := src/main
MAIN_HEADER_FILES := $(call find_in_dir_with_suffix,include,hpp)
MAIN_INLINE_FILES := $(call find_in_dir_with_suffix,include,inl)
MAIN_IMPL_FILES := $(call find_in_dir_with_suffix,$(MAIN_SOURCE_ROOT_DIR),cpp)
MAIN_OBJECTS = $(addprefix $(OBJECT_DIR)/,$(subst src/,,$(MAIN_IMPL_FILES:.cpp=.o)))
MAIN_OBJECT_DIRS = $(sort $(dir $(MAIN_OBJECTS)))
MAIN_TARGET_LIBRARY := $(OUTPUT_DIR)/libtux.a

# Test
TEST_SOURCE_ROOT_DIR := src/test
TEST_IMPL_FILES := $(call find_in_dir_with_suffix,$(TEST_SOURCE_ROOT_DIR),cpp)
TEST_OBJECTS := $(addprefix $(OBJECT_DIR)/,$(subst src/,,$(TEST_IMPL_FILES:.cpp=.o)))
TEST_OBJECT_DIRS := $(sort $(dir $(TEST_OBJECTS)))
TEST_BINARY := $(TEST_EXEC_DIR)/run-tests

# GTest
GTEST_DIR := externals/gtest
GTEST_INCLUDE := $(GTEST_DIR)/include
GTEST_HEADERS := $(call find_in_dir_with_suffix,$(GTEST_INCLUDE),h)
GTEST_IMPLS := $(call find_in_dir_with_suffix,$(GTEST_DIR)/src,cc)
GTEST_LIB_DIR := $(GTEST_DIR)/lib
GTEST_LIB := $(GTEST_LIB_DIR)/libgtest.a

all: $(MAIN_TARGET_LIBRARY) run_test

clean:
	rm -rf $(OUTPUT_DIR)/*

main_init:
	@mkdir -p $(MAIN_OBJECT_DIRS) $(OUTPUT_DIR)

$(MAIN_TARGET_LIBRARY): $(MAIN_OBJECTS)
	ar rcs $(MAIN_TARGET_LIBRARY) $(MAIN_OBJECTS)

$(OBJECT_DIR)/main/%.o: Makefile | main_init
	@echo "building $*.cpp"
	@$(call compile,src/main/$*.cpp,main/$*.o)

run_test: $(TEST_BINARY)
	./$(TEST_BINARY)

test_init:
	mkdir -p $(TEST_OBJECT_DIRS) $(TEST_EXEC_DIR)

$(TEST_BINARY): $(TEST_OBJECTS) $(GTEST_LIB)
	@echo "creating $(TEST_BINARY)"
	$(CXX) -o $(TEST_BINARY) $(COMPILE_FLAGS) -Iexternals/gtest/include $(TEST_OBJECTS) -L$(OUTPUT_DIR) -L$(GTEST_LIB_DIR) -ltux -lgtest

$(OBJECT_DIR)/test/%.o: $(MAIN_TARGET_LIBRARY) $(MAIN_HEADER_FILES) $(MAIN_INLINE_FILES) Makefile | test_init
	@echo "building $*.cpp"
	@$(call compile,src/test/$*.cpp,test/$*.o) -Iexternals/gtest/include

$(GTEST_LIB): $(GTEST_HEADERS) $(GTEST_IMPLS)
	@mkdir -p $(GTEST_LIB_DIR)
	$(CXX) -I$(GTEST_INCLUDE) -I$(GTEST_DIR) -c $(GTEST_DIR)/src/gtest-all.cc -o $(GTEST_LIB_DIR)/gtest-all.o -pthread
	ar rcs $(GTEST_LIB) $(GTEST_LIB_DIR)/gtest-all.o

%.d:

.PHONY: all clean main_init run_test test_init

define depends_include_template
-include $(1)

endef

$(eval $(foreach dep,$(wildcard $(MAIN_OBJECTS:.o=.d)),$(call depends_include_template,$(dep))))

